#include <stdlib.h>
#include "Arbre.h"
#include "Symbole.h"
#include "SymboleValue.h"

////////////////////////////////////////////////////////////////////////////////
// NoeudSeqInst
////////////////////////////////////////////////////////////////////////////////

NoeudSeqInst::NoeudSeqInst() : tabInst() {
}

////////////////////////////////////////////////////////////////////////////////
int NoeudSeqInst::getValeur() {
  int valeur=0;
  for (unsigned int i=0; i<tabInst.size(); i++)
    valeur = tabInst[i]->getValeur();  // on evalue chaque instruction de la séquence
  return valeur; // par convention, resultat = valeur de la derniere instruction
}

////////////////////////////////////////////////////////////////////////////////
void NoeudSeqInst::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Sequence de " << tabInst.size() << " instruction(s)" << endl;
  for (unsigned int i=0; i<tabInst.size(); i++)
    tabInst[i]->afficher(indentation+1); // on affiche les fils en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
void NoeudSeqInst::ajouteInstruction(Noeud* instruction) {
  tabInst.push_back(instruction);
}

////////////////////////////////////////////////////////////////////////////////
// NoeudAffectation
////////////////////////////////////////////////////////////////////////////////

NoeudAffectation::NoeudAffectation(Noeud* variable, Noeud* expression) {
  this->variable=variable;
  this->expression=expression;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudAffectation::getValeur() {
  int valeur = expression->getValeur(); // on évalue l'expression
  ((SymboleValue*)variable)->setValeur(valeur); // on affecte la variable
  return valeur; // par convention, une affectation a pour valeur la valeur affectée
}

////////////////////////////////////////////////////////////////////////////////
void NoeudAffectation::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Affectation" << endl;
  variable->afficher(indentation+1);   // on affiche variable et expression
  expression->afficher(indentation+1); // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurBinaire
////////////////////////////////////////////////////////////////////////////////

NoeudOperateurBinaire::NoeudOperateurBinaire ( Symbole operateur,
                                               Noeud* operandeGauche,
                                               Noeud* operandeDroit) {
  this->operateur=operateur;
  this->operandeGauche=operandeGauche;
  this->operandeDroit=operandeDroit;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudOperateurBinaire::getValeur() { 
  int valeur=0;
  int og=operandeGauche->getValeur();
  int od=operandeDroit->getValeur();
  if      (this->operateur=="+") valeur = og+od; 
  else if (this->operateur=="-") valeur = og-od;
  else if (this->operateur=="*") valeur = og*od; 
  else if (this->operateur=="/") {
	if (od!=0)
		valeur = og/od;
  	else {
	  	cout << "Erreur pendant l'interpretation : division par zero" << endl;
	  	exit(0); // plus tard on levera une exception
  	}
    }
  else if (this->operateur=="et") valeur = og&od;
  else if (this->operateur=="ou") valeur = og|od;
  else if (this->operateur=="-") valeur = -od;
  else if (this->operateur=="non") valeur = !od;
  else if (this->operateur=="==") valeur = (og==od);
  else if (this->operateur=="!=") valeur = (og!=od);
  else if (this->operateur=="<") valeur = (og<od);
  else if (this->operateur=="<=") valeur = (og<=od);
  else if (this->operateur==">") valeur = (og>od);
  else if (this->operateur==">=") valeur = (og>=od);
  else { // this->operateur est inconnu
	  	cout << "Erreur pendant l'interpretation" << endl;
	  	exit(0); // plus tard on levera une exception
  	}
  return valeur;
}

////////////////////////////////////////////////////////////////////////////////
void NoeudOperateurBinaire::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Operateur Binaire \"" << this->operateur.getChaine() << "\" applique a : " << endl;
  operandeGauche->afficher(indentation+1);  // on affiche fils gauche et fils droit
  operandeDroit->afficher(indentation+1);   // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
// NoeudCondition
////////////////////////////////////////////////////////////////////////////////

NoeudCondition::NoeudCondition( Noeud* exp,
                                Noeud* tabInst) : casExp(), casTabInst() {
	this->casExp.push_back(exp);
	this->casTabInst.push_back(tabInst);
}

////////////////////////////////////////////////////////////////////////////
void NoeudCondition::ajouterCasExp(Noeud* casExp){
	this->casExp.push_back(casExp);
}

////////////////////////////////////////////////////////////////////////////
void NoeudCondition::ajouterCasTabInst(Noeud* casTabInst){
	this->casTabInst.push_back(casTabInst);
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstSi
////////////////////////////////////////////////////////////////////////////////

NoeudInstSi::NoeudInstSi(   Noeud* expBool,
                            Noeud* tabInst): NoeudCondition(expBool,tabInst) {
}

////////////////////////////////////////////////////////////////////////////////
int NoeudInstSi::getValeur() {
  int valeur=0;                 // Valeur nulle (FAUX) par défaut
  int expB=0;                   // Expression booléenne
  bool fini = false;            // On n'a pas fini
  for (unsigned int i=0; i<casExp.size() && !fini; i++){// On analyse chaque expressions booléennes "sinonsi"
      if (casExp[i] == NULL) {		// Si il y a un "sinon"
          valeur=casTabInst[i]->getValeur();   // On récupère la valeur
          fini=true;                            // On indique que l'on a fini
      } else {
	  expB = casExp[i]->getValeur();                // Expression booléenne suivante
	  if (expB==1) {                                // Si elle est verifiée
              valeur = casTabInst[i]->getValeur();  // On récupère la valeur
              fini=true;                            // On indique que l'on a fini
          }
      }
  }
  return valeur;     // on retourne la valeur
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstSi::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Instruction Si "<< endl;
  Noeud::afficher(indentation);
  cout << "Instruction si "<< endl;
  for (unsigned int i=0; i<casExp.size(); i++){
      if (casExp[i] == NULL) {
	  Noeud::afficher(indentation);
	  cout <<" Instruction sinon "<<endl;
	  casTabInst[i]->afficher(indentation+1);
      } else {
	  Noeud::afficher(indentation);
	  cout <<"Instruction sinonsi "<<endl;
          casExp[i]->afficher(indentation+1);
          casTabInst[i]->afficher(indentation+1);
      }
  }
  Noeud::afficher(indentation);
  cout <<" Instruction finsi "<<endl;
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstSelon
////////////////////////////////////////////////////////////////////////////////

NoeudInstSelon::NoeudInstSelon( Noeud* expBool,
                                Noeud* tabInst,
                                Noeud* variable): NoeudCondition(expBool,tabInst) {
	this->variable=variable;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudInstSelon::getValeur() {
  int valeur=0;                 // Valeur nulle (FAUX) par défaut
  int cas=0;                    // Cas qui est traité
  bool fini = false;            // On n'a pas fini
  for (unsigned int i=0; i<casExp.size() && !fini; i++){// On analyse chaque expressions booléennes "sinonsi"
      if (casExp[i] == NULL) {		// Si il y a un "defaut"
          valeur=casTabInst[i]->getValeur();   // On récupère la valeur
          fini=true;                            // On indique que l'on a fini
      } else {
	  cas = casExp[i]->getValeur();            // Cas suivant
	  if (variable->getValeur() == cas) {      // Si elle est verifiée
              valeur = casTabInst[i]->getValeur();  // On récupère la valeur
              fini=true;                            // On indique que l'on a fini
          }
      }
  }
  return valeur;     // on retourne la valeur
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstSelon::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Instruction Selon "<< endl;
  Noeud::afficher(indentation);
  cout << "Instruction cas "<< endl;
  for (unsigned int i=0; i<casExp.size(); i++){
      if (casExp[i] == NULL) {
	  Noeud::afficher(indentation);
	  cout <<" Instruction defaut "<<endl;
	  casTabInst[i]->afficher(indentation+1);
      } else {
	  Noeud::afficher(indentation);
	  cout <<"Instruction cas "<<endl;
          casExp[i]->afficher(indentation+1);
          casTabInst[i]->afficher(indentation+1);
      }
  }
  Noeud::afficher(indentation);
  cout <<" Instruction finselon "<<endl;
}

////////////////////////////////////////////////////////////////////////////////
// NoeudBoucle
////////////////////////////////////////////////////////////////////////////////

NoeudBoucle::NoeudBoucle(   Noeud* expBool,
                            Noeud* tabInst) {
  this->expBool=expBool;
  this->tabInst=tabInst;
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstTq
////////////////////////////////////////////////////////////////////////////////

NoeudInstTq::NoeudInstTq(   Noeud* expBool,
                            Noeud* tabInst) : NoeudBoucle(expBool,tabInst){

}

////////////////////////////////////////////////////////////////////////////////
int NoeudInstTq::getValeur() {
  int valeur=0;
  int boolean = expBool->getValeur();
  while (boolean==1) {
      valeur = tabInst->getValeur();
      boolean = expBool->getValeur();
  }
  return valeur; // on retourne la valeur
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstTq::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Instruction TantQue  "<< endl;
  Noeud::afficher(indentation);
  cout << "Instruction tantque  "<< endl;
  expBool->afficher(indentation+1);
  tabInst->afficher(indentation+1);
  Noeud::afficher(indentation);
  cout<<"Instruction fintantque";
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstRepeter
////////////////////////////////////////////////////////////////////////////////

NoeudInstRepeter::NoeudInstRepeter( Noeud* expBool,
                                    Noeud* tabInst) : NoeudBoucle(expBool,tabInst){

}

////////////////////////////////////////////////////////////////////////////////
int NoeudInstRepeter::getValeur() {
    int valeur=0;
    int boolean = expBool->getValeur();
    do{
        valeur= tabInst->getValeur();
        boolean = expBool->getValeur();
    }while(boolean==0);
    return valeur; // valeur = valeur de la dernière instruction
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstRepeter::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Instruction Repeter  "<< endl;
  Noeud::afficher(indentation);
  cout << "Instruction repeter  "<< endl;
  expBool->afficher(indentation+1);
  tabInst->afficher(indentation+1);
  Noeud::afficher(indentation);
  cout<<"Instruction jusqua";
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstPour
////////////////////////////////////////////////////////////////////////////////

NoeudInstPour::NoeudInstPour(   Noeud* expBool,
                                Noeud* tabInst,
                                Noeud* affect,
                                Noeud* affectInc) : NoeudBoucle(expBool,tabInst){
    this->affect = affect;
    this->affectInc = affectInc;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudInstPour::getValeur() {
    int valeur=0;
    int boolean;
    valeur=affect->getValeur();
    boolean = expBool->getValeur();
    while (boolean==1){
        valeur = tabInst->getValeur();
        valeur = affectInc->getValeur();
        boolean = expBool->getValeur();
    }
    return valeur; // valeur = valeur de la derni�re instruction
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstPour::afficher(unsigned short indentation) {
    Noeud::afficher(indentation);
    cout << "Noeud - Instruction Pour  "<< endl;
    Noeud::afficher(indentation);
    cout <<"Instruction pour";
    affect->afficher(indentation+1);
    expBool->afficher(indentation+1);
    affectInc->afficher(indentation+1);
    Noeud::afficher(indentation);
    cout<<" finpour ";
    tabInst->afficher(indentation+1);
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstLire
////////////////////////////////////////////////////////////////////////////////

NoeudInstLire::NoeudInstLire(Noeud* variable)  {
	this->variable=variable;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudInstLire::getValeur() {
	  int valeur=0;
	  valeur=variable->getValeur();

	  return valeur; // valeur de la derniere instruction
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstLire::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Instruction lire  "<< endl;
  Noeud::afficher(indentation);
  cout <<"Instruction Lire";
  variable->afficher(indentation+1);
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstEcrire
////////////////////////////////////////////////////////////////////////////////

NoeudInstEcrire::NoeudInstEcrire(Noeud* expChaine)  {
  this->expChaine=expChaine;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudInstEcrire::getValeur() {
	  int valeur=0;
	  valeur=expChaine->getValeur();
          cout << valeur << endl;
	  return valeur; // valeur de la derniere instruction
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstEcrire::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Instruction ecrire "<< endl;
  Noeud::afficher(indentation);
  cout <<"Instruction ecrire";
  expChaine->afficher(indentation+1);
}